#include <fs.h>
#include <log.h>
#include <ds/lru.h>
#include <mm/slab.h>

extern kmem_cache_t* inode_kmem_cache;
extern lru_t* dentry_lru, * inode_lru;

inode_t* fs_find_inode(sb_t* sb, u32 id) {
    hbkt_t* slot = &sb->inode_cache[id & FS_HASH_MASK];
    if (slot->head) {
        inode_t* pos, * n;
        bucket_foreach(slot, pos, n, hash_list) {
            if (pos->id == id) {
                lru_use_one(inode_lru, &pos->lru);
                return pos;
            }
        }
    }
    return NULL;
}

inode_t* fs_alloc_inode(sb_t* sb) {
    assert(sb->init_inode);

    inode_t* inode;
    if (!(inode = take_obj(inode_kmem_cache))) {
        lru_evict_half(inode_lru);
        if (!(inode = take_obj(inode_kmem_cache))) {
            return NULL;
        }
    }

    memset(inode, 0, sizeof(*inode));
    mutex_init(&inode->lock);

    sb->init_inode(sb, inode);
    inode->sb = sb;

    lru_use_one(inode_lru, &inode->lru);
    return inode;
}

void fs_assign_inode(dentry_t* dentry, inode_t* inode) {
    if (dentry->inode) {
        dentry->inode->link_count--;
    }
    dentry->inode = inode;
    inode->link_count++;
}

void fs_free_inode(inode_t* inode) {
    if (inode->pcache) {
        // TODO: release page cache
    }
    hlist_remove(&inode->hash_list);
    release_obj(inode_kmem_cache, inode);
}

void fs_inode_addhash(inode_t* inode) {
    hbkt_t* slot = &inode->sb->inode_cache[inode->id & FS_HASH_MASK];
    hlist_remove(&inode->hash_list);
    hlist_add(&slot->head, &inode->hash_list);
}